Some R packages we will be using for the workshop. Feel free to add others during the session.

The dataset we will be using is the State Expenditures data set, which you can load into your R session by passing the URL to RSocrata::read.socrata().

About the data: https://data.oregon.gov/Revenue-Expense/Agency-Expenditures-Multi-Year-Report/y9g9-xsxs/about_data

This report provides information on expenditures (i.e., cash transactions/payments) for the agencies that utilize the Statewide Financial Management Application (SFMA) issued for the fiscal year 2024 (July 1, 2023 - June 30, 2024).

Purpose

Before defining our purpose we should consider…

Who might be interested in an analysis of expenditures?

  • Public employees
  • Agencies/agency heads
  • Public/taxpayers
  • Legislators
  • Vendors/Suppliers
  • Auditors
  • Analysts

What are the benefits?

  • Process improvements (internal and external)
  • Viewing outliers (identifying too small or too large of expenditures)
  • Trends, e.g. seeing the general pattern - what are the highest/lowest expenditures, what are the most common expenses, etc.
  • Compliance, to ensure that we are following our procurement rules
  • Opportunities for consolidating, getting better contracts
  • Better understanding of where taxes go

What are the risks?

  • Legislators may see the analysis and think that costs are too high and use it as rationale to cut budgets
  • Patterns may be revealed that harm reputations (institutional, individual, etc)

Purpose: Make the expenditures data set more accessible to those interested in the analysis by providing

Expense trends

  • Overall expenses by year
  • Expense by year by agency
  • Expense by category by agency
  • Expense by category by vendor

Deep dive into outliers

  • High & Low expenditures
  • Expenses out of compliance with procurement statues/rules/guidance

Viewing the data

We do not build an analysis around the data we have; we find the data for the analysis that we need!

This chunk shows two ways we can load the data, one relies on an API via RSocrata, whereas the second option relies on exporting the data manually from Data.Oregon.Gov.

# Using RSocrata
state_expenditures <- read.socrata("https://data.oregon.gov/Revenue-Expense/Agency-Expenditures-Multi-Year-Report/y9g9-xsxs")

# Using csv file stored locally
#state_expenditures <- read.csv(here("data", "Agency_Expenditures_–_Multi-Year_Report_20250827.csv"), stringsAsFactors = FALSE)

glimpse(state_expenditures)
Rows: 567,728
Columns: 10
$ fiscal_year    <int> 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 20…
$ agency         <int> 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, 9…
$ agency_1       <chr> "REAL ESTATE AGY", "REAL ESTATE AGY", "REAL ESTATE AGY", "REAL ESTATE AGY", "REAL ESTA…
$ budget_class   <int> 3110, 3240, 4100, 4100, 4100, 4100, 4100, 4100, 4100, 4100, 4100, 4100, 4100, 4100, 41…
$ budget_class_1 <chr> "CLASS/UNCLASS SALARY & PER DIEM", "UNEMPLOYMENT ASSESSMENT", "INSTATE TRAVEL", "INSTA…
$ expend_class   <int> 3111, 3231, 4101, 4101, 4101, 4101, 4101, 4104, 4105, 4106, 4106, 4108, 4108, 4108, 41…
$ expend_class_1 <chr> "REGULAR EMPLOYEES", "UNEMPLOYMENT COMPENSATION & ASSESSMENT", "INSTATE MEALS WITH OVE…
$ vendor         <chr> "DEPARTMENT OF ADMINISTRATIVE SERVICES", "EMPLOYMENT DEPARTMENT", "FRANK LEONARD JR", …
$ expense        <dbl> 270.72, 5131.00, 59.00, 73.75, 88.50, 295.00, 458.13, 120.00, 414.01, 898.28, 2843.22,…
$ vendor_st      <chr> "OR", "OR", "", "", "", "", "", "MO", "MO", "", "MO", "", "", "", "", "", "OR", "MO", …

Data quality check

We’ll clean-up some of the types that should be treated as characters. We’ll also rename variables for improved readability.

state_expenditures_clean <- 
  state_expenditures %>% 
  mutate(
    agency = as.character(agency),
    expend_class = as.character(expend_class),
    budget_class = as.character(budget_class)
  ) %>% 
  rename(
    "agency_code" = agency,
    "agency_name" = agency_1,
    "budget_class_code" = budget_class,
    "buget_class_name" = budget_class_1,
    "expend_class_code" = expend_class,
    "expend_class_name" = expend_class_1
  )

glimpse(state_expenditures_clean)
Rows: 567,728
Columns: 10
$ fiscal_year       <int> 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,…
$ agency_code       <chr> "919", "919", "919", "919", "919", "919", "919", "919", "919", "919", "919", "919",…
$ agency_name       <chr> "REAL ESTATE AGY", "REAL ESTATE AGY", "REAL ESTATE AGY", "REAL ESTATE AGY", "REAL E…
$ budget_class_code <chr> "3110", "3240", "4100", "4100", "4100", "4100", "4100", "4100", "4100", "4100", "41…
$ buget_class_name  <chr> "CLASS/UNCLASS SALARY & PER DIEM", "UNEMPLOYMENT ASSESSMENT", "INSTATE TRAVEL", "IN…
$ expend_class_code <chr> "3111", "3231", "4101", "4101", "4101", "4101", "4101", "4104", "4105", "4106", "41…
$ expend_class_name <chr> "REGULAR EMPLOYEES", "UNEMPLOYMENT COMPENSATION & ASSESSMENT", "INSTATE MEALS WITH …
$ vendor            <chr> "DEPARTMENT OF ADMINISTRATIVE SERVICES", "EMPLOYMENT DEPARTMENT", "FRANK LEONARD JR…
$ expense           <dbl> 270.72, 5131.00, 59.00, 73.75, 88.50, 295.00, 458.13, 120.00, 414.01, 898.28, 2843.…
$ vendor_st         <chr> "OR", "OR", "", "", "", "", "", "MO", "MO", "", "MO", "", "", "", "", "", "OR", "MO…

We can use inspectdf to view the unique counts, and most common values for each of the categorical variables.

state_expenditures_clean %>% 
  inspect_cat()

 Column (2/8):  agency_name
 Column (3/8):  budget_class_code
                                                                                
 Column (4/8):  buget_class_name
 Column (5/8):  expend_class_code
 Column (6/8):  expend_class_name
                                                                                
 Column (7/8):  vendor
 Column (8/8):  vendor_st
                                                                                

We can see that there are discrepancies between the agency, budget, and expenditure class codes since the total counts for these are different. Let’s take a look at those.

state_expenditures_clean %>% 
  distinct(agency_code, agency_name) %>% 
  count(agency_code, sort = TRUE) %>% 
  filter(n > 1)


state_expenditures_clean %>% 
  distinct(budget_class_code, buget_class_name) %>% 
  count(budget_class_code, sort = TRUE) %>% 
  filter(n > 1)


state_expenditures_clean %>% 
  distinct(expend_class_code, expend_class_name) %>% 
  count(expend_class_code, sort = TRUE) %>% 
  filter(n > 1)

At this point we may decide that the data quality issues require engagement with the data owners of this asset to proceed with our analysis.

Or we may be comfortable with proceeding!

Exploratory Analysis

Viewing total expenses by year

state_expenditures_clean %>% 
  group_by(fiscal_year) %>% 
  reframe(total_expenses_in_billions = sum(expense)/1e9)
state_expenditures_clean %>% 
  group_by(fiscal_year) %>% 
  reframe(total_expenses_in_billions = sum(expense)/1e9) %>% 
  ggplot(aes(x = as.factor(fiscal_year), y = total_expenses_in_billions)) + 
  geom_col() +
  scale_y_continuous(labels = scales::label_currency()) +
  theme_classic() + 
  labs(
    y = "Total Expenses ($ Billions)", 
    x = NULL,
    caption = "\nData source:\n \'Agency Expenditures - Multi-Year Report\'\n As of December 17, 2024 from Data.Oregon.Gov"
  )

How about viewing by agencies with the top 5 highest expenses in 2019?

top_agencies <- 
  state_expenditures_clean %>% 
  filter(fiscal_year == 2024) %>% 
  group_by(agency_name) %>% 
  reframe(total_expenses = sum(expense)) %>% 
  arrange(-total_expenses) %>% 
  slice(1:5) %>% 
  pull(agency_name)

summary_by_top_agencies <- 
  state_expenditures_clean %>% 
  filter(agency_name %in% top_agencies) %>% 
  group_by(fiscal_year, agency_name) %>% 
  reframe(total_expenses_in_billions = sum(expense)/1e9) %>% 
  mutate(
    agency_name = fct_reorder(agency_name, total_expenses_in_billions, .desc = TRUE)
  )
  
summary_by_top_agencies  %>% 
  ggplot(aes(x = as.factor(fiscal_year), y = total_expenses_in_billions, group = agency_name)) + 
  geom_col() +
  facet_wrap(~ agency_name, ncol = 3, scales = "free_y", axes = "margins") + 
  scale_y_continuous(labels = scales::label_currency()) +
  theme_classic() + 
  labs(
    y = "Total Expenses ($ Billions)", 
    x = NULL,
    caption = "\nData source:\n \'Agency Expenditures - Multi-Year Report\'\n As of December 17, 2024 from Data.Oregon.Gov",
    title = "Agencies with the Top Highest Expenditures in 2024",
    subtitle = "Axes for total expenses varies by agency to show differences in scale."
  )

8.27.2025
Amelia L. Vargas

LS0tDQp0aXRsZTogIlNPUkEgSW5jbHVzaXZlIEFuYWx5dGljcyBXb3Jrc2hvcCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNClNvbWUgUiBwYWNrYWdlcyB3ZSB3aWxsIGJlIHVzaW5nIGZvciB0aGUgd29ya3Nob3AuIEZlZWwgZnJlZSB0byBhZGQgb3RoZXJzIGR1cmluZyB0aGUgc2Vzc2lvbi4gDQoNCmBgYHtyIHNldHVwfQ0KbGlicmFyeShoZXJlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoUlNvY3JhdGEpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGluc3BlY3RkZikNCmxpYnJhcnkoZm9yY2F0cykNCmBgYA0KDQpUaGUgZGF0YXNldCB3ZSB3aWxsIGJlIHVzaW5nIGlzIHRoZSBTdGF0ZSBFeHBlbmRpdHVyZXMgZGF0YSBzZXQsIHdoaWNoIHlvdSBjYW4gbG9hZCBpbnRvIHlvdXIgUiBzZXNzaW9uIGJ5IHBhc3NpbmcgdGhlIFVSTCB0byBgUlNvY3JhdGE6OnJlYWQuc29jcmF0YSgpYC4gDQoNCkFib3V0IHRoZSBkYXRhOiANCjxodHRwczovL2RhdGEub3JlZ29uLmdvdi9SZXZlbnVlLUV4cGVuc2UvQWdlbmN5LUV4cGVuZGl0dXJlcy1NdWx0aS1ZZWFyLVJlcG9ydC95OWc5LXhzeHMvYWJvdXRfZGF0YT4NCg0KVGhpcyByZXBvcnQgcHJvdmlkZXMgaW5mb3JtYXRpb24gb24gZXhwZW5kaXR1cmVzIChpLmUuLCBjYXNoIHRyYW5zYWN0aW9ucy9wYXltZW50cykgZm9yIA0KdGhlIGFnZW5jaWVzIHRoYXQgdXRpbGl6ZSB0aGUgU3RhdGV3aWRlIEZpbmFuY2lhbCBNYW5hZ2VtZW50IEFwcGxpY2F0aW9uIChTRk1BKSBpc3N1ZWQgDQpmb3IgdGhlIGZpc2NhbCB5ZWFyIDIwMjQgKEp1bHkgMSwgMjAyMyAtIEp1bmUgMzAsIDIwMjQpLiANCg0KIyBQdXJwb3NlDQoNCkJlZm9yZSBkZWZpbmluZyBvdXIgcHVycG9zZSB3ZSBzaG91bGQgY29uc2lkZXIuLi4gDQoNCiMjIFdobyBtaWdodCBiZSBpbnRlcmVzdGVkIGluIGFuIGFuYWx5c2lzIG9mIGV4cGVuZGl0dXJlcz8gDQoNCiogUHVibGljIGVtcGxveWVlcyAgDQoqIEFnZW5jaWVzL2FnZW5jeSBoZWFkcyAgDQoqIFB1YmxpYy90YXhwYXllcnMgIA0KKiBMZWdpc2xhdG9ycyAgDQoqIFZlbmRvcnMvU3VwcGxpZXJzICANCiogQXVkaXRvcnMgIA0KKiBBbmFseXN0cw0KDQoNCiMjIFdoYXQgYXJlIHRoZSBiZW5lZml0cz8NCg0KKiBQcm9jZXNzIGltcHJvdmVtZW50cyAoaW50ZXJuYWwgYW5kIGV4dGVybmFsKSAgDQoqIFZpZXdpbmcgb3V0bGllcnMgKGlkZW50aWZ5aW5nIHRvbyBzbWFsbCBvciB0b28gbGFyZ2Ugb2YgZXhwZW5kaXR1cmVzKSANCiogVHJlbmRzLCBlLmcuIHNlZWluZyB0aGUgZ2VuZXJhbCBwYXR0ZXJuIC0gd2hhdCBhcmUgdGhlIGhpZ2hlc3QvbG93ZXN0IGV4cGVuZGl0dXJlcywgd2hhdCBhcmUgdGhlIG1vc3QgY29tbW9uIGV4cGVuc2VzLCBldGMuDQoqIENvbXBsaWFuY2UsIHRvIGVuc3VyZSB0aGF0IHdlIGFyZSBmb2xsb3dpbmcgb3VyIHByb2N1cmVtZW50IHJ1bGVzDQoqIE9wcG9ydHVuaXRpZXMgZm9yIGNvbnNvbGlkYXRpbmcsIGdldHRpbmcgYmV0dGVyIGNvbnRyYWN0cw0KKiBCZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiB3aGVyZSB0YXhlcyBnbw0KDQojIyBXaGF0IGFyZSB0aGUgcmlza3M/IA0KDQoqIExlZ2lzbGF0b3JzIG1heSBzZWUgdGhlIGFuYWx5c2lzIGFuZCB0aGluayB0aGF0IGNvc3RzIGFyZSB0b28gaGlnaCBhbmQgdXNlIGl0IGFzIHJhdGlvbmFsZSB0byBjdXQgYnVkZ2V0cyAgDQoqIFBhdHRlcm5zIG1heSBiZSByZXZlYWxlZCB0aGF0IGhhcm0gcmVwdXRhdGlvbnMgKGluc3RpdHV0aW9uYWwsIGluZGl2aWR1YWwsIGV0YykNCg0KUHVycG9zZTogDQpNYWtlIHRoZSBleHBlbmRpdHVyZXMgZGF0YSBzZXQgbW9yZSBhY2Nlc3NpYmxlIHRvIHRob3NlIGludGVyZXN0ZWQgaW4gdGhlIGFuYWx5c2lzIGJ5IHByb3ZpZGluZw0KDQpFeHBlbnNlIHRyZW5kcyAgDQoNCiogT3ZlcmFsbCBleHBlbnNlcyBieSB5ZWFyICANCiogRXhwZW5zZSBieSB5ZWFyIGJ5IGFnZW5jeSAgDQoqIEV4cGVuc2UgYnkgY2F0ZWdvcnkgYnkgYWdlbmN5ICANCiogRXhwZW5zZSBieSBjYXRlZ29yeSBieSB2ZW5kb3IgIA0KDQpEZWVwIGRpdmUgaW50byBvdXRsaWVycyAgDQoNCiogSGlnaCAmIExvdyBleHBlbmRpdHVyZXMgIA0KKiBFeHBlbnNlcyBvdXQgb2YgY29tcGxpYW5jZSB3aXRoIHByb2N1cmVtZW50IHN0YXR1ZXMvcnVsZXMvZ3VpZGFuY2UNCg0KIyBWaWV3aW5nIHRoZSBkYXRhICANCg0KKldlIGRvIG5vdCBidWlsZCBhbiBhbmFseXNpcyBhcm91bmQgdGhlIGRhdGEgd2UgaGF2ZTsgd2UgZmluZCB0aGUgZGF0YSBmb3IgdGhlIGFuYWx5c2lzIHRoYXQgd2UgbmVlZCEqDQoNClRoaXMgY2h1bmsgc2hvd3MgdHdvIHdheXMgd2UgY2FuIGxvYWQgdGhlIGRhdGEsIG9uZSByZWxpZXMgb24gYW4gQVBJIHZpYSBSU29jcmF0YSwgd2hlcmVhcyB0aGUgc2Vjb25kIG9wdGlvbiByZWxpZXMgb24gZXhwb3J0aW5nIHRoZSBkYXRhIG1hbnVhbGx5IGZyb20gDQpEYXRhLk9yZWdvbi5Hb3YuDQoNCmBgYHtyIGxvYWQtZXhwZW5kaXR1cmUtZGF0YSwgZWNobz1UUlVFfQ0KIyBVc2luZyBSU29jcmF0YQ0Kc3RhdGVfZXhwZW5kaXR1cmVzIDwtIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLm9yZWdvbi5nb3YvUmV2ZW51ZS1FeHBlbnNlL0FnZW5jeS1FeHBlbmRpdHVyZXMtTXVsdGktWWVhci1SZXBvcnQveTlnOS14c3hzIikNCg0KIyBVc2luZyBjc3YgZmlsZSBzdG9yZWQgbG9jYWxseQ0KI3N0YXRlX2V4cGVuZGl0dXJlcyA8LSByZWFkLmNzdihoZXJlKCJkYXRhIiwgIkFnZW5jeV9FeHBlbmRpdHVyZXNf4oCTX011bHRpLVllYXJfUmVwb3J0XzIwMjUwODI3LmNzdiIpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCmdsaW1wc2Uoc3RhdGVfZXhwZW5kaXR1cmVzKQ0KYGBgDQojIyBEYXRhIHF1YWxpdHkgY2hlY2sNCg0KV2UnbGwgY2xlYW4tdXAgc29tZSBvZiB0aGUgdHlwZXMgdGhhdCBzaG91bGQgYmUgdHJlYXRlZCBhcyBjaGFyYWN0ZXJzLiBXZSdsbCBhbHNvIHJlbmFtZSB2YXJpYWJsZXMgZm9yIGltcHJvdmVkIHJlYWRhYmlsaXR5Lg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpzdGF0ZV9leHBlbmRpdHVyZXNfY2xlYW4gPC0gDQogIHN0YXRlX2V4cGVuZGl0dXJlcyAlPiUgDQogIG11dGF0ZSgNCiAgICBhZ2VuY3kgPSBhcy5jaGFyYWN0ZXIoYWdlbmN5KSwNCiAgICBleHBlbmRfY2xhc3MgPSBhcy5jaGFyYWN0ZXIoZXhwZW5kX2NsYXNzKSwNCiAgICBidWRnZXRfY2xhc3MgPSBhcy5jaGFyYWN0ZXIoYnVkZ2V0X2NsYXNzKQ0KICApICU+JSANCiAgcmVuYW1lKA0KICAgICJhZ2VuY3lfY29kZSIgPSBhZ2VuY3ksDQogICAgImFnZW5jeV9uYW1lIiA9IGFnZW5jeV8xLA0KICAgICJidWRnZXRfY2xhc3NfY29kZSIgPSBidWRnZXRfY2xhc3MsDQogICAgImJ1Z2V0X2NsYXNzX25hbWUiID0gYnVkZ2V0X2NsYXNzXzEsDQogICAgImV4cGVuZF9jbGFzc19jb2RlIiA9IGV4cGVuZF9jbGFzcywNCiAgICAiZXhwZW5kX2NsYXNzX25hbWUiID0gZXhwZW5kX2NsYXNzXzENCiAgKQ0KDQpnbGltcHNlKHN0YXRlX2V4cGVuZGl0dXJlc19jbGVhbikNCmBgYA0KV2UgY2FuIHVzZSBgaW5zcGVjdGRmYCB0byB2aWV3IHRoZSB1bmlxdWUgY291bnRzLCBhbmQgbW9zdCBjb21tb24gdmFsdWVzIGZvciBlYWNoIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIA0KDQpgYGB7ciBlY2hvPVRSVUV9DQpzdGF0ZV9leHBlbmRpdHVyZXNfY2xlYW4gJT4lIA0KICBpbnNwZWN0X2NhdCgpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBkaXNjcmVwYW5jaWVzIGJldHdlZW4gdGhlIGFnZW5jeSwgYnVkZ2V0LCBhbmQgZXhwZW5kaXR1cmUgY2xhc3MgY29kZXMgc2luY2UgdGhlIHRvdGFsIGNvdW50cyBmb3IgdGhlc2UgYXJlIGRpZmZlcmVudC4gDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aG9zZS4gDQoNCmBgYHtyIGlkZW50aWZ5LWR1cGxpY2F0ZXMsIGVjaG89VFJVRX0NCnN0YXRlX2V4cGVuZGl0dXJlc19jbGVhbiAlPiUgDQogIGRpc3RpbmN0KGFnZW5jeV9jb2RlLCBhZ2VuY3lfbmFtZSkgJT4lIA0KICBjb3VudChhZ2VuY3lfY29kZSwgc29ydCA9IFRSVUUpICU+JSANCiAgZmlsdGVyKG4gPiAxKQ0KDQoNCnN0YXRlX2V4cGVuZGl0dXJlc19jbGVhbiAlPiUgDQogIGRpc3RpbmN0KGJ1ZGdldF9jbGFzc19jb2RlLCBidWdldF9jbGFzc19uYW1lKSAlPiUgDQogIGNvdW50KGJ1ZGdldF9jbGFzc19jb2RlLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBmaWx0ZXIobiA+IDEpDQoNCg0Kc3RhdGVfZXhwZW5kaXR1cmVzX2NsZWFuICU+JSANCiAgZGlzdGluY3QoZXhwZW5kX2NsYXNzX2NvZGUsIGV4cGVuZF9jbGFzc19uYW1lKSAlPiUgDQogIGNvdW50KGV4cGVuZF9jbGFzc19jb2RlLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBmaWx0ZXIobiA+IDEpDQpgYGANCg0KDQpBdCB0aGlzIHBvaW50IHdlIG1heSBkZWNpZGUgdGhhdCB0aGUgZGF0YSBxdWFsaXR5IGlzc3VlcyByZXF1aXJlIGVuZ2FnZW1lbnQgd2l0aCB0aGUgZGF0YSBvd25lcnMgb2YgdGhpcyBhc3NldCB0byBwcm9jZWVkIHdpdGggb3VyIGFuYWx5c2lzLiANCg0KT3Igd2UgbWF5IGJlIGNvbWZvcnRhYmxlIHdpdGggcHJvY2VlZGluZyENCg0KIyBFeHBsb3JhdG9yeSBBbmFseXNpcw0KDQpWaWV3aW5nIHRvdGFsIGV4cGVuc2VzIGJ5IHllYXINCg0KYGBge3IgZWNobz1UUlVFfQ0Kc3RhdGVfZXhwZW5kaXR1cmVzX2NsZWFuICU+JSANCiAgZ3JvdXBfYnkoZmlzY2FsX3llYXIpICU+JSANCiAgcmVmcmFtZSh0b3RhbF9leHBlbnNlc19pbl9iaWxsaW9ucyA9IHN1bShleHBlbnNlKS8xZTkpDQpgYGANCg0KYGBge3IgZWNobz1UUlVFfQ0Kc3RhdGVfZXhwZW5kaXR1cmVzX2NsZWFuICU+JSANCiAgZ3JvdXBfYnkoZmlzY2FsX3llYXIpICU+JSANCiAgcmVmcmFtZSh0b3RhbF9leHBlbnNlc19pbl9iaWxsaW9ucyA9IHN1bShleHBlbnNlKS8xZTkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gYXMuZmFjdG9yKGZpc2NhbF95ZWFyKSwgeSA9IHRvdGFsX2V4cGVuc2VzX2luX2JpbGxpb25zKSkgKyANCiAgZ2VvbV9jb2woKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmxhYmVsX2N1cnJlbmN5KCkpICsNCiAgdGhlbWVfY2xhc3NpYygpICsgDQogIGxhYnMoDQogICAgeSA9ICJUb3RhbCBFeHBlbnNlcyAoJCBCaWxsaW9ucykiLCANCiAgICB4ID0gTlVMTCwNCiAgICBjYXB0aW9uID0gIlxuRGF0YSBzb3VyY2U6XG4gXCdBZ2VuY3kgRXhwZW5kaXR1cmVzIC0gTXVsdGktWWVhciBSZXBvcnRcJ1xuIEFzIG9mIERlY2VtYmVyIDE3LCAyMDI0IGZyb20gRGF0YS5PcmVnb24uR292Ig0KICApDQpgYGANCg0KDQpIb3cgYWJvdXQgdmlld2luZyBieSBhZ2VuY2llcyB3aXRoIHRoZSB0b3AgNSBoaWdoZXN0IGV4cGVuc2VzIGluIDIwMTk/IA0KDQpgYGB7ciBlY2hvPVRSVUV9DQp0b3BfYWdlbmNpZXMgPC0gDQogIHN0YXRlX2V4cGVuZGl0dXJlc19jbGVhbiAlPiUgDQogIGZpbHRlcihmaXNjYWxfeWVhciA9PSAyMDI0KSAlPiUgDQogIGdyb3VwX2J5KGFnZW5jeV9uYW1lKSAlPiUgDQogIHJlZnJhbWUodG90YWxfZXhwZW5zZXMgPSBzdW0oZXhwZW5zZSkpICU+JSANCiAgYXJyYW5nZSgtdG90YWxfZXhwZW5zZXMpICU+JSANCiAgc2xpY2UoMTo1KSAlPiUgDQogIHB1bGwoYWdlbmN5X25hbWUpDQoNCnN1bW1hcnlfYnlfdG9wX2FnZW5jaWVzIDwtIA0KICBzdGF0ZV9leHBlbmRpdHVyZXNfY2xlYW4gJT4lIA0KICBmaWx0ZXIoYWdlbmN5X25hbWUgJWluJSB0b3BfYWdlbmNpZXMpICU+JSANCiAgZ3JvdXBfYnkoZmlzY2FsX3llYXIsIGFnZW5jeV9uYW1lKSAlPiUgDQogIHJlZnJhbWUodG90YWxfZXhwZW5zZXNfaW5fYmlsbGlvbnMgPSBzdW0oZXhwZW5zZSkvMWU5KSAlPiUgDQogIG11dGF0ZSgNCiAgICBhZ2VuY3lfbmFtZSA9IGZjdF9yZW9yZGVyKGFnZW5jeV9uYW1lLCB0b3RhbF9leHBlbnNlc19pbl9iaWxsaW9ucywgLmRlc2MgPSBUUlVFKQ0KICApDQogIA0Kc3VtbWFyeV9ieV90b3BfYWdlbmNpZXMgICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gYXMuZmFjdG9yKGZpc2NhbF95ZWFyKSwgeSA9IHRvdGFsX2V4cGVuc2VzX2luX2JpbGxpb25zLCBncm91cCA9IGFnZW5jeV9uYW1lKSkgKyANCiAgZ2VvbV9jb2woKSArDQogIGZhY2V0X3dyYXAofiBhZ2VuY3lfbmFtZSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlX3kiLCBheGVzID0gIm1hcmdpbnMiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpsYWJlbF9jdXJyZW5jeSgpKSArDQogIHRoZW1lX2NsYXNzaWMoKSArIA0KICBsYWJzKA0KICAgIHkgPSAiVG90YWwgRXhwZW5zZXMgKCQgQmlsbGlvbnMpIiwgDQogICAgeCA9IE5VTEwsDQogICAgY2FwdGlvbiA9ICJcbkRhdGEgc291cmNlOlxuIFwnQWdlbmN5IEV4cGVuZGl0dXJlcyAtIE11bHRpLVllYXIgUmVwb3J0XCdcbiBBcyBvZiBEZWNlbWJlciAxNywgMjAyNCBmcm9tIERhdGEuT3JlZ29uLkdvdiIsDQogICAgdGl0bGUgPSAiQWdlbmNpZXMgd2l0aCB0aGUgVG9wIEhpZ2hlc3QgRXhwZW5kaXR1cmVzIGluIDIwMjQiLA0KICAgIHN1YnRpdGxlID0gIkF4ZXMgZm9yIHRvdGFsIGV4cGVuc2VzIHZhcmllcyBieSBhZ2VuY3kgdG8gc2hvdyBkaWZmZXJlbmNlcyBpbiBzY2FsZS4iDQogICkNCmBgYA0KDQo4LjI3LjIwMjUgIA0KQW1lbGlhIEwuIFZhcmdhcw==